/**
 * 定义了所有涉及修改磁盘文件的工具方法
*/
#include "defines.h"

void writeInodeBmap(int pos, bool used);

/**
 * 获取磁盘文件流
*/
fstream getDisk()
{
    fstream disk(FNAME, ios::binary | ios::in | ios::out);
    return disk;
}

/**
 * 获取磁盘大小（单位：块
*/
int getDiskSize()
{
    fstream disk = getDisk();
    disk.seekg(0, ios::end);
    int size = disk.tellg() / 1024;
    disk.close();
    return size;
}

/**
 * 获取磁盘状态信息
*/
DiskStatus getDiskStatus()
{
    fstream disk = getDisk();
    DiskStatus ds;
    ds.diskSize = getDiskSize();
    //获取i节点使用情况
    ds.usedInode = 0;
    int x;
    for (int i = 0; i < INODENUM / 8; ++i)
    {
        char c = inodeBmap[i];
        x = castChar2Int(c);
        while (x != 0)
        {
            ds.usedInode += 1;
            x = x & (x - 1);
        }
    }
    //获取文件块使用情况
    ds.usedBlk = 0;
    for (int i = 0; i < BLKNUM / 8 + 1; ++i)
    {
        char c = fblkBmap[i];
        x = castChar2Int(c);
        while (x != 0)
        {
            ds.usedBlk += 1;
            x = x & (x - 1);
        }
    }
    disk.close();
    return ds;
}

/**
 * 清除所有磁盘内容
*/
void clearDisk()
{
    system("dd if=/dev/zero of=./disk bs=1M count=100");
    return;
}

/**
 * 读取磁盘指定位置的一个字符串(也可是一个char,缺省len参数)
 * @param pos 字符串位置
 * @param len 字符串长度
*/
string readStr(int pos, int len = 1)
{
    fstream disk = getDisk();
    disk.seekg(pos);
    char *s = new char[len];
    disk.read(s, len);
    string str = s;
    if (DEBUG)
    {
        cout << '[' << __FUNCTION__ << ']';
        cout << "string: '" << str << "'\n";
    }
    disk.close();
    return str;
}

/**
 * 向磁盘指定位置写入一个字符数组(也可是一个char,缺省len参数)
 * @param pos 字符串位置
 * @param str 字符串指针
 * @param len 字符串长度
*/
char writeStr(int pos, char *str, int len = 1)
{
    fstream disk = getDisk();
    disk.seekp(pos);
    //int len=strlen(str);
    if (DEBUG)
    {
        cout << '[' << __FUNCTION__ << ']';
        cout << "tellp: " << disk.tellp() /*<<"\tstr:"<<str*/ << " strlen:\t" << len << endl;
    }
    disk.write(str, len);
    disk.close();
    return *str;
}

/**
 * 读取i节点位图数组中特定位
 * @param pos 要读取的位序号
*/
char readInodeBmap(int pos)
{
    //字符数组中下标
    int bytepos = pos / 8;
    char byteChar = inodeBmap[bytepos];
    string byteBin = castChar2Bin(byteChar);
    //byte中位置
    int posInByte = pos % 8;
    char *bit = (char *)byteBin.substr(posInByte, 1).data();
    if (DEBUG)
    {
        cout << '[' << __FUNCTION__ << ']';
        cout << "inodebmap bit at " << pos << ": '" << *bit << "'\n";
    }
    return *bit;
}

/**
 * 修改i节点位图中特定位，数组、磁盘均改
 * @param pos i节点在位图中位置，即其序号
*/
void writeInodeBmap(int pos, bool used)
{
    //字符数组中下标
    int bytepos = pos / 8;
    //byte中位置
    int posInByte = pos % 8;
    //获取旧的char
    char byteChar = inodeBmap[bytepos];
    //转为二进制字符串
    string byteBin = castChar2Bin(byteChar);
    //替换对应位
    byteBin.replace(posInByte, 1, used ? "1" : "0");
    char *byte = (char *)byteBin.data();
    //数组中对应位置替换为新的char
    char newByteChar = castBin2Char(byte);
    inodeBmap[bytepos] = newByteChar;
    //磁盘中位置，加上前面超级块的大小1kB
    int diskpos = bytepos + KB;
    writeStr(diskpos, &newByteChar);
    if (DEBUG)
    {
        cout << '[' << __FUNCTION__ << ']';
        cout << (used ? "'1" : "'0") << "' write to pos: " << pos << endl;
    }
}

/**
 * 读取空闲块位图数组中特定位
 * @param pos 要读取的位序号
*/
char readFblkBmap(int pos)
{
    //字符数组中下标
    int bytepos = pos / 8;
    char byteChar = fblkBmap[bytepos];
    string byteBin = castChar2Bin(byteChar);
    //byte中位置
    int posInByte = pos % 8;
    char *bit = (char *)byteBin.substr(posInByte, 1).data();
    if (DEBUG)
    {
        cout << '[' << __FUNCTION__ << ']';
        cout << "fblkbmap bit at " << pos << ": '" << *bit << "'\n";
    }
    return *bit;
}

/**
 * 修改文件块位图中特定位，数组、磁盘均改
 * @param pos 是i节点在位图中位置
*/
void writeFblkBmap(int pos, bool used)
{
    //字符数组中下标
    int bytepos = pos / 8;
    //byte中位置
    int posInByte = pos % 8;
    //获取旧的char
    char byteChar = fblkBmap[bytepos];
    //转为二进制字符串
    string byteBin = castChar2Bin(byteChar);
    //替换对应位
    byteBin.replace(posInByte, 1, used ? "1" : "0");
    char *byte = (char *)byteBin.data();
    //数组中对应位置替换为新的char
    char newByteChar = castBin2Char(byte);
    fblkBmap[bytepos] = newByteChar;
    //磁盘中位置，加上前面超级块、i节点位图2kB
    int diskpos = bytepos + 2 * KB;
    writeStr(diskpos, &newByteChar);
    if (DEBUG)
    {
        cout << '[' << __FUNCTION__ << ']';
        cout << (used ? "'1" : "'0") << "' write to pos: " << pos << endl;
    }
}

/**
 * 向磁盘写入一整块
 * @param data 要写入的块数据
 * @param blockpos 要写入的磁盘目的块号
*/
void saveBlock(char *data, int blockpos)
{
    fstream disk = getDisk();
    disk.seekp(blockpos * KB);
    disk.write(data, KB);
    disk.close();
    return;
}

/**
 * 从磁盘读入一整块
 * @param blockpos 块地址
*/
void getBlock(char *buff, int blockpos)
{
    fstream disk = getDisk();
    disk.seekg(blockpos * KB);
    disk.read(buff, KB);
    disk.close();
    return;
}

/**
 * 向缓存块指定位置写入一个字符数组(也可是一个char,缺省len参数)
 * @param blk 要被写入的块
 * @param posinb 要写入的块内起始位置
 * @param str 待写入的字符串
 * @param len 待写入字符串的长度
*/
void writeStr2Blk(char *blk, int posinb, char *str, int len = 1)
{
    if (posinb + len > KB)
    {
        cout << '[' << __FUNCTION__ << ']' << endl;
        cout << "error: pos too big\n";
        return;
    }
    for (int i = 0; i < len; ++i)
    {
        *(blk + posinb + i) = *(str + i);
    }
    return;
}

/**
 * 向磁盘指定位置写入一个int
 * @param pos 要写入的磁盘位置
 * @param a 要写入的int
*/
void writeInt(int pos, int a)
{
    char *achar = (char *)&a;
    writeStr(pos, achar, sizeof(int));
    if (DEBUG)
    {
        cout << '[' << __FUNCTION__ << ']' << endl;
        cout << "pos: " << pos << "\tint:" << a << endl;
    }
}

/**
 * 向缓存块指定位置写入一个int
 * @param blk 待写入的块
 * @param pos 写入起始位置
 * @param a 要写入的int
*/
void writeInt2Blk(char *blk, int pos, int a)
{
    char *achar = (char *)&a;
    writeStr2Blk(blk, pos, achar, sizeof(int));
    if (DEBUG)
    {
        cout << '[' << __FUNCTION__ << ']';
        cout << "pos: " << pos << "\tint:" << a << endl;
    }
}

/**
 * 向磁盘写入一个文件i节点
 * @param n 需要写入的i节点
 * @param nodepos 文件i节点的序号（0~2047
*/
void writeFileInode(fileiNode n, int nodepos)
{
    if (nodepos >= INODENUM)
    {
        cout << '[' << __FUNCTION__ << ']';
        cout << "nodepos too big!" << endl;
        return;
    }
    char buff[KB]{};
    int pos = 0;
    writeInt2Blk(buff, pos, n.nodeNo);           //i节点编号
    writeStr2Blk(buff, pos + 4, &n.fileSize);    //文件大小
    writeStr2Blk(buff, pos + 5, &n.protectCode); //保护码
    writeInt2Blk(buff, pos + 6, n.createTime);   //创建时间
    writeInt2Blk(buff, pos + 10, n.lastCatTime); //上次打开时间
    //写入文件磁盘块地址
    for (int i = 0; i < castChar2Int(n.fileSize); ++i)
    {
        writeInt2Blk(buff, 16 + 4 * i, n.blockAddr[i]);
    }
    //将缓存块存入磁盘
    saveBlock(buff, INODESTART + nodepos);
    //更新位图
    writeInodeBmap(nodepos, 1);
    return;
}

/**
 * 从磁盘读取一个文件i节点
 * @param nodepos 文件i节点的序号（0~2047
*/
fileiNode readFileInode(int nodepos)
{
    fileiNode n;
    if (nodepos >= INODENUM)
    {
        cout << '[' << __FUNCTION__ << ']';
        cout << "nodepos too big! given pos:" <<nodepos<< endl;
        return n;
    }
    char buff[KB]{};
    getBlock(buff, INODESTART + nodepos);
    if (castChar2Int(buff[3]) != 0)
    {
        cout << '[' << __FUNCTION__ << ']';
        cout << "not a file inode!" << endl;
        return n;
    }
    n.nodeNo = nodepos;
    n.fileSize = buff[4];
    n.protectCode = buff[5];
    n.createTime = *(int *)(buff + 6);
    n.lastCatTime = *(int *)(buff + 10);
    int addr;
    for (int i = 0; i < castChar2Int(n.fileSize); ++i)
    {
        addr = *(int *)(buff + 16 + 4 * i);
        n.blockAddr[i] = addr;
    }
    return n;
}

/**
 * 向磁盘写入一个文件i节点
 * @param n 需要写入的i节点
 * @param nodepos i节点在i节点区中的位置
*/
void writeFolderInode(folderiNode n, int nodepos)
{
    if (nodepos >= INODENUM)
    {
        cout << '[' << __FUNCTION__ << ']';
        cout << "nodepos too big!" << endl;
        return;
    }
    char buff[KB]{};
    writeInt2Blk(buff, 0, n.nodeNo);         //i节点编号
    writeStr2Blk(buff, 4, n.folderName, 28); //文件夹名
    writeStr2Blk(buff, 32, &n.folderSize);   //文件夹大小
    writeStr2Blk(buff, 33, &n.protectCode);  //保护码
    writeInt2Blk(buff, 34, n.createTime);    //创建时间
    writeInt2Blk(buff, 38, n.lastCatTime);   //上次打开时间
    //写入磁盘块地址TODO
    for (int i = 0; i < castChar2Int(n.folderSize); ++i)
    {
        writeInt2Blk(buff, 64 + 32 * i, n.subItems[i].nodeNo);
        writeStr2Blk(buff, 64 + 32 * i + 4, n.subItems[i].name, 28);
    }
    saveBlock(buff, INODESTART + nodepos);
    //更新位图
    writeInodeBmap(nodepos, 1);
    return;
}

/**
 * 从磁盘读取一个目录i节点TODO
 * @param nodepos 目录i节点的序号（0~2047
*/
folderiNode readFolderInode(int nodepos)
{
    folderiNode n;
    if (nodepos >= INODENUM)
    {
        cout << '[' << __FUNCTION__ << ']';
        cout << "nodepos too big!" << endl;
        return n;
    }
    char buff[KB]{};
    getBlock(buff, INODESTART + nodepos);
    if (castChar2Int(buff[3]) == 0)
    {
        cout << '[' << __FUNCTION__ << ']';
        cout << "not a folder inode!" << endl;
        return n;
    }
    n.nodeNo = nodepos + FOLDERSIGN;
    strncpy(n.folderName, buff + 4, 28);
    n.folderSize = buff[32];
    n.protectCode = buff[33];
    n.createTime = *(int *)(buff + 34);
    n.lastCatTime = *(int *)(buff + 34);
    FolderItem fi;
    for (int i = 0; i < castChar2Int(n.folderSize); ++i)
    {
        fi.nodeNo = *(int *)(buff + 64 + 32 * i);
        strncpy(fi.name, buff + 64 + 32 * i + 4, 28);
        n.subItems[i] = fi;
    }
    return n;
}

//将磁盘中位图加载到内存
void loadBitMap(){
    char *buff=new char[KB]{};
    //读取i节点位图
    getBlock(buff, 1);
    memcpy(inodeBmap,buff,KB);
    //读取文件块位图
    for(int i = 2; i<=14; ++i){
        getBlock(buff, i);
        memcpy(fblkBmap+(i-2)*KB, buff, KB);
    }
    //cout<<"bitmap loaded."<<endl;
}

//格式化一个磁盘文件
DiskStatus formatDisk()
{
    clearDisk();
    loadBitMap();
    fstream disk = getDisk();
    DiskStatus ds;
    ds.diskSize = getDiskSize();
    //创建超级块
    int sizeInt = ds.diskSize;
    writeInt(0, sizeInt);
    //建立根目录i节点
    folderiNode n;
    n.nodeNo = 0 + FOLDERSIGN;
    string fname = "root";
    strcpy(n.folderName, fname.c_str());
    n.folderSize = 0;
    n.protectCode = (char)63;
    n.createTime = getIntTime();
    n.lastCatTime = 0;
    n.addSubItem(1, "alohaaaaaaaaa.txt");
    n.addSubItem(2, "guide.txt");
    writeFolderInode(n, 0);
    //建立alohaaaaaaaaa.txt的i节点
    fileiNode na;
    na.nodeNo = 1;
    na.fileSize = 0;
    na.protectCode = (char)63;
    na.createTime = getIntTime();
    na.lastCatTime = 0;
    na.addBlock(0);
    writeFileInode(na, 1);
    //建立guide.txt的i节点
    fileiNode ng;
    ng.nodeNo = 2;
    ng.fileSize = 0;
    ng.protectCode = (char)63;
    ng.createTime = getIntTime();
    ng.lastCatTime = 0;
    ng.addBlock(1);
    writeFileInode(ng, 2);
    //建立alohaaaaaaaaa.txt内容磁盘块
    char *blk = new char[KB]{};
    strcpy(blk, aloha.c_str());
    saveBlock(blk, BLKSTART);
    writeFblkBmap(0, 1);
    //建立guide.txt内容磁盘块
    delete (blk);
    blk = new char[KB]{};
    strcpy(blk, guide.c_str());
    saveBlock(blk, BLKSTART + 1);
    writeFblkBmap(1, 1);
    //关闭磁盘
    disk.close();
    cout << "disk formatted!\n";
    return ds;
}